package com.example.itcacp.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.itcacp.entity.Message;
import com.example.itcacp.repository.mybatis.MessageMapper;
import com.example.itcacp.repository.mybatis.UserMapper;
import com.example.itcacp.util.SpringContextHolder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;

import java.sql.Timestamp;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
@ServerEndpoint ("/ws/{token}" )
@CrossOrigin(origins = "*")
//前端连接端点 ws://localhost:8000/ws/{用户的token，用于解析和认证用户信息}
public class WebSocketServer {

    /**
     * 静态变量：用于记录当前在线连接数，线程安全的类
     * */
    private static final AtomicInteger onlineSessionClientCount = new AtomicInteger (0);
    /**
     * 存放所有在线的客户端
     * */
    private final static Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();
    /**
     * 连接token和连接会话：用户身份令牌
     * */
//     新增：用户ID到Token的映射
    private static final Map<String, String> userIdToTokenMap = new ConcurrentHashMap<>();
    private String token;
    private Session session;
    //JWT签名
    private static final String SECRET = "1234567890-abcdefghijklmnopqrstuvwxyz"; // 与JWTInterceptor保持一致
    private String userId; // 用户ID
    private String roleName;// 用户角色

    // 移除静态变量，使用实例变量
    private MessageMapper messageMapper;

    // 添加无参构造器
    public WebSocketServer() {
    }

    // 在需要使用 MessageMapper 的方法中获取 Bean
    private MessageMapper getMessageMapper() {
        if (messageMapper == null) {
            messageMapper = SpringContextHolder.getBean(MessageMapper.class);
        }
        return messageMapper;
    }

    /**
     * 连接成功调用的方法，由前端<code>new WebSocket</code>触发
     *
     * @param token  每次页面建立连接时传入到服务器的token，可自定义
     * @param session  与某个客户端的连接会话，需要通过它来给客户端发送消息
     * */
    @OnOpen
    public void onOpen(@PathParam ("token") String token, Session session) {
        // session.getId(): 当前session会话自动生成一个id，从0开始累加
        log.info ( "连接建立中====> session_id = {}, sToken = {}", session.getId (), token );
        try {
        // 验证JWT
        verifyJwtToken ( token );
        //加入Map中，将页面的token和session绑定或者session.getId()与session
        //加入onlineSessionClientMap.put(session.getId(), session);
        onlineSessionClientMap.put ( token, session );
        // 在线数 + 1
        onlineSessionClientCount.incrementAndGet ();
        this.token = token;
        this.session = session;
        // TODO 在真实环境中，这里需要解析token => 得到一个用户信息 => 在连接成功之后 => 推送对应的用户信息返回前端
        // 发送认证成功及用户信息
        String userInfo = "{\"userId\":\"" + userId + "\",\"role\":\"" + roleName + "\"}";
        sendToOne ( token, "认证连接成功" + userInfo );
        // 保存用户ID与Token的映射
        userIdToTokenMap.put(userId, token);
        log.info ( "连接建立成功，当前在线数为： {} ===> 开始监听新连接：session_id = {}, sToken = {},.", onlineSessionClientCount, session.getId (), token );
    } catch (JWTVerificationException e) {
            log.error("JWT验证失败: {}", e.getMessage());
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.VIOLATED_POLICY, "认证失败"));
            } catch (Exception ex) {
                log.error("关闭会话失败", ex);
            }
        } catch (Exception e) {
            log.error("连接建立异常", e);
            try {
                System.out.println ("服务器内部错误");
            } catch (Exception ex) {
                log.error("关闭会话失败", ex);
            }
        }
    }
    private void verifyJwtToken(String token) throws JWTVerificationException {
        if (token == null || token.trim().isEmpty()) {
            throw new JWTVerificationException("Token为空");
        }
        // 使用与JWTInterceptor相同的算法和密钥
        Algorithm algorithm = Algorithm.HMAC256(SECRET);
        JWTVerifier verifier = JWT.require(algorithm).build();
        verifier.verify(token);
        // 解析用户信息
        String audience = JWT.decode(token).getAudience().get(0);
        String[] parts = audience.split("-");
        if (parts.length != 2) {
            throw new JWTVerificationException("无效的令牌格式");
        }
        this.userId = parts[0];
        this.roleName = parts[1];
    }
    // 检查用户是否已登录（示例实现）
    private boolean isUserAlreadyLoggedIn() {
        // 实际项目中可能需要查询缓存或数据库
        return onlineSessionClientMap.containsKey(session.getId ());
    }


    /**
     * 连接关闭调用的方法，由前端<code>socket.close()</code>触发
     *
     * @param token  每次页面建立连接时传入到服务器的token，可自定义
     * @param session  与某个客户端的连接会话，需要通过它来给客户端发送消息
     * */
    @OnClose
    public void onClose(@PathParam ( "token" ) String token, Session session) {
        // 从映射中移除用户
        String userId = getUserIdFromToken(token);
        userIdToTokenMap.remove(userId);
        // 从Map中移除
        onlineSessionClientMap.remove ( token );
        // 在线人数减1
        onlineSessionClientCount.decrementAndGet ();
        log.info ( "连接关闭成功，当前在线数量为：{} ===> 关闭该连接信息： session_id = {}, sToken = {},.", onlineSessionClientCount, session.getId (), token );
    }

    /**
     * 收到客户端调用的方法，由前端<code>socket.send</code>触发
     *  */
    @OnMessage
    public void onMessage(String message, Session session) throws JsonProcessingException {
        /*
         * html界面传递过来的数据格式，可以自定义
         * {"token":"user-1", "message":"hello websocket"}
         * */
//        log.info ( "接收到消息------------"+message );
        // A给B发送消息，A要知道B的消息，发送消息的时候把B的信息携带过来
        log.info ( "服务端收到客户端消息 ===> fromSToken = {}, message = {}", token, message);
        // 后台拿到前端 发送的数据后，后台得告诉前端 我已经成功接收到
        sendToOne ( token, message );  // 发送原始数据
        // 1. 解析前端JSON数据
        ObjectMapper objectMapper = new ObjectMapper ();
        Message readValue = objectMapper.readValue ( message, Message.class );

        System.out.println (readValue);
        Message me = new Message ();
        me.setSenderId ( readValue.getSenderId () );
        me.setReceiverId ( readValue.getReceiverId () );
        me.setContent ( readValue.getContent () );
        me.setMessageType ( readValue.getMessageType () );
        me.setStatus("未读");
        me.setCreatedAt( new Timestamp ( System.currentTimeMillis () ).toLocalDateTime () );
        getMessageMapper ().insert ( me );

        String receiverId = String.valueOf ( readValue.getReceiverId() );
        String receiverToken = userIdToTokenMap.get(receiverId); // 通过接收者ID获取Token
        String senderToken = userIdToTokenMap.get ( readValue.getSenderId () );
        if (receiverToken != null) {
            // 发送消息给接收者
            sendToOne(receiverToken, readValue.getContent());
            log.info("消息已推送给接收者，receiverId: {}", receiverId);
            sendToOne ( senderToken, readValue.getContent () );
        } else {
            log.info("接收者不在线，消息已保存，receiverId: {}", receiverId);
        }
    }

    private String getUserIdFromToken(String token) {
        try {
            return JWT.decode(token).getAudience().get(0).split("-")[0];
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 发生错误调用得方法
     * */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error ( "WebSocket发生错误，错误信息为：" + error.getMessage ());
        error.printStackTrace ();
    }

    /**
     * 指定发送消息
     * */
    public int sendToOne(String toToken, String message) {
        /*
         * 判断发送者是否在线
         * */
        Session toSession = onlineSessionClientMap.get ( toToken );
        if (toSession == null) {
            log.info ( "服务端给客户端发送消息 ==> tosToken = {} 不存在， message = {}", toToken, message );
            return 2;
        }
        // 异步发送
        log.info ( "服务端给客户端发送消息 ==> tosToken = {}， message = {}", toToken, message );
        // 发送到用户端
        toSession.getAsyncRemote ().sendText(message);
        return 1;
    }

    /**
     * 群发消息
     * */
    public void sendToAll(String message) {
        // 遍历在线map集合
        onlineSessionClientMap.forEach ( (onlineSid, toSession) -> {
            // 排除掉自己
            if (!token.equalsIgnoreCase ( onlineSid )) {
                log.info ( "服务端给客户端群发消息 ===> token = {}, toSid = {}, message = {}", token, onlineSid, message );
                toSession.getAsyncRemote ().sendText ( message );
            }
        } );
    }

}
